<template>
  <div class="image-viewer hg-images-previewer-back" @mousewheel="scaleImg" v-show="show" :style="{zIndex}"
       @keydown.left.capture="prevImg" @keydown.right.capture="nextImg">
    <img class="icon" v-show="loadStatus==='404'" :src="load404 || icons.default404" alt="加载中...">
    <img class="icon" v-show="loadStatus==='loading'" :src="loading || icons.defaultLoading" alt="加载中...">
    <img @error="loadStatus='404'" @load="loadStatus='loaded'" @loadstart="loadStatus='loading'" v-show="loadStatus==='loaded'"
         :style="{cursor, zoom, top:`${top}%`, left:`${left}%`, transform: `translate(-50%, -50%) scale(${flipX},${flipY}) rotate(${rotate}deg)`}"
         class="img" :src="curimg" @mousemove="moveZoomer" draggable @dragstart.prevent=""/>
    <img class="zoomer" v-if="zoomer" :src="curimg" alt="放大镜">
    <div class="previewer-title">
      <slot v-if="title" name="title">
        <div @click="showIndexPrompt">{{index+1}}/{{images.length}}</div>
      </slot>
    </div>
    <div class="previewer-close" @click.stop="closePreview">
      <img :src="icons.close" alt="关闭" />
    </div>
    <el-button v-if="pager" @click.stop="prevImg" class="btn-preview-prev" type="info" icon="el-icon-arrow-left" circle></el-button>
    <el-button v-if="pager" @click.stop="nextImg" class="btn-preview-next" type="info" icon="el-icon-arrow-right" circle></el-button>
    <div v-if="galleryBar" v-show="gallery" class="preview-gallery" @mousewheel.stop="">
      <img :style="{zoom: idx === index?1.25:1}" @click.stop="index = idx" v-for="(thumb, idx) in images" :src="`${baseUrl}/${thumb}`" alt="缩略图"
           :key="`thumb-${idx}`">
    </div>
    <div class="previewer-toolbox" v-show="toolbar">
      <div v-if="toolbars === 'all' || toolbars.includes('rotate')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="rotateImg(90)">
        <img :src="icons.rotate" alt="旋转" style="width: 30px;height:30px"/>
      </div>
      <div v-if="toolbars === 'all' || toolbars.includes('rotate')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="rotateImg(-90)">
        <img :src="icons.rotateAnti" alt="旋转"/>
      </div>
      <div v-if="toolbars === 'all' || toolbars.includes('resize')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="zoom=1">
        <img :src="icons.resize" alt="1:1"/>
      </div>
      <div v-if="toolbars === 'all' || toolbars.includes('gallery')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="gallery = !gallery">
        <img :src="icons.gallery" alt="画廊"/>
      </div>
      <template v-if="toolbars === 'all' || toolbars.includes('play')">
        <div v-if="!timer" class="btn-circle" style="margin: 0 10px!important;" @click.stop="playImgs">
          <img :src="icons.play" alt="播放"/>
        </div>
        <div v-else class="btn-circle" style="margin: 0 10px!important;" @click.stop="stopPlay">
          <img :src="icons.pause" alt="暂停"/>
        </div>
      </template>
      <div v-if="toolbars === 'all' || toolbars.includes('scale')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="scaleImg({}, 100)">
        <img :src="icons.scaledown" alt="缩小"/>
      </div>
      <div v-if="toolbars === 'all' || toolbars.includes('sacle')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="scaleImg({}, -100)">
        <img :src="icons.scaleup" alt="放大"/>
      </div>
      <div v-if="toolbars === 'all' || toolbars.includes('flip')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="flipX*=-1">
        <img :src="icons.flipX" alt="水平翻转"/>
      </div>
      <div v-if="toolbars === 'all' || toolbars.includes('flip')" class="btn-circle" style="margin: 0 10px!important;" @click.stop="flipY*=-1">
        <img :src="icons.flipY" alt="垂直翻转"/>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  name: "hg-images-previewer",
  watch:{
    loadStatus(newStatus){
      if(newStatus === '404'){
        this.$emit('error')
      }
    },

    initialIndex(newIdx){
      this.index = newIdx
    },

    /***
     * 当索引改变时，重置当前的状态
     */
    index(newIndex){
      this.zoom = 1
      this.rotate = 0
      this.left = 50
      this.top = 50
      this.$emit('change', {thumb: this.images[newIndex], preview: this.curimg, index:newIndex, source:'pager'})
    },

    /***
     * 监听自动播放状态
     */
    playing(newPlay){
      if(newPlay){      // 开启了自动播放功能
        this.playImgs(null, this.interval)
      }else{            // 停止自动播放
        this.stopPlay()
      }
    },

    /***
     * 重新定义计时器
     */
    interval(newInterval){
      if(this.playing){
        this.stopPlay()
        this.playImgs(null, newInterval)
      }
    }
  },
  data(){
    return{
      show:false,
      index:0,            // 当前所浏览的索引
      cursor: 'grab',     // 鼠标指针
      zoom: 1,            // 当前的缩放值
      rotate:0,           // 旋转角度
      flipX:1,            // 水平翻转
      flipY:1,            // 垂直翻转
      zoomer:false,       // 是否开启放大镜工具
      timer: null,        // 自动播放的定时器,
      top:50,             // 图片位置
      left:50,            // 图片位置
      gallery: true,      // 画廊工具栏
      icons:{
        rotate:require('./images/toolbox/rotate.svg'),
        rotateAnti:require('./images/toolbox/rotate-anti.svg'),
        resize:require('./images/toolbox/1_1.svg'),
        play:require('./images/toolbox/play.svg'),
        pause:require('./images/toolbox/pause.svg'),
        scaleup:require('./images/toolbox/scaleup.svg'),
        scaledown:require('./images/toolbox/scaledown.svg'),
        gallery:require('./images/toolbox/gallery.svg'),
        flipX:require('./images/toolbox/flip-hor.svg'),
        flipY:require('./images/toolbox/flip-vert.svg'),
        close:require('./images/close.svg'),
        defaultLoading:require('./images/loading.svg'),
        default404: require('./images/404.svg'),
      },
      loadStatus:'loading', // 图片读取状态：loading，loaded，404
    }
  },
  computed:{
    /***
     * 返回当前图片的信息
     */
    curimg(){
      // 返回原来图片
      if(this.previewUrl === null){
        return `${this.baseUrl}/${this.images[this.index]}`
      }
      // 假如是数组，则返回对应的数组下的钩子地址（仅支持字符串数组）
      if(this.previewUrl instanceof Array){
        return `${this.baseUrl}/${this.previewUrl[this.index]}`
      }
      switch (typeof(this.previewUrl)) {
          // 调用钩子函数
        case "function":
          return `${this.baseUrl}/${this.previewUrl(this.images[this.index], this.index)}`
        // 直接替换原url
        case "string":
          return `${this.baseUrl}/${this.previewUrl}`
        // object被解析为JSON，在预览时，会替换掉原来的查询字符串
        case "object":
          // eslint-disable-next-line no-case-declarations
          const [url, ] = this.images[this.index].split('?')
          // eslint-disable-next-line no-case-declarations
          const qs = []
          for (const key in Object.getOwnPropertyNames(this.previewUrl)) {
            qs.push(`${key}=${this.previewUrl[key]}`)
          }
          return `${this.baseUrl}/${url}?${JSON.stringify(qs.join('&'))}`
        default:
          // 调用toString()方法
          return `${this.baseUrl}/${this.previewUrl.toString(this.images[this.index], this.index)}`
      }
    },

    /***
     * 返回当前图片的下载地址
     */
    downloadImg(){
      // 返回原来图片
      if(this.downloadUrl === null){
        return `${this.baseUrl}/${this.images[this.index]}`
      }
      // 假如是数组，则返回对应的数组下的钩子地址（仅支持字符串数组）
      if(this.downloadUrl instanceof Array){
        return `${this.baseUrl}/${this.downloadUrl[this.index]}`
      }
      switch (typeof(this.downloadUrl)) {
          // 调用钩子函数
        case "function":
          return `${this.baseUrl}/${this.downloadUrl(this.images[this.index], this.index)}`
        case "string":
          return `${this.baseUrl}/${this.downloadUrl}`
          // object被解析为JSON，在预览时，会替换掉原来的查询字符串
        case "object":
          // eslint-disable-next-line no-case-declarations
          const [url, ] = this.images[this.index].split('?')
          // eslint-disable-next-line no-case-declarations
          const qs = []
          for (const key in this.downloadUrl) {
            qs.push(`${key}=${this.downloadUrl[key]}`)
          }
          return `${this.baseUrl}/${url}?${JSON.stringify(qs.join('&'))}`
        default:
          // 调用toString()方法
          return `${this.baseUrl}/${this.downloadUrl.toString(this.images[this.index], this.index)}`
      }
    }
  },
  props:{
    // 图片基础请求基础网址url
    baseUrl: {
      type: String,
      default:"",
    },
    // 需要预览的图片的地址集合（不包含基础域名和路径）
    images:{
      type: Array,
      default:()=>[]
    },
    // 一个钩子函数，用于转换当前需要预览的图片高清地址
    previewUrl:{
      type: [Function, String, Array, null, Object],
      default:null
    },
    // 一个钩子函数，用于转换当前图片地址为下载地址
    downloadUrl:{
      type: Function,
      default: null,
    },
    // 图片的初始预览索引
    initialIndex:{
      type:Number,
      default:0,
    },
    // 图片预览的zindex层级
    zIndex:{
      type:Number,
      default:2000,
    },
    // 工具栏需要显示的功能：rotate（旋转），scale（缩放），flip（镜像），play（自动播放），resize（还原），download（下载），zoom（放大镜），gallery（画廊）
    toolbars:{
      type: [Array, String],
      default:'all'
    },
    // 工具栏是否可见
    toolbar:{
      type: Boolean,
      default: true,
    },
    // 是否自动播放图片
    playing: {
      type: Boolean,
      default: false
    },
    // 自动播放图片的时间间隔，单位秒
    interval:{
      type:Number,
      default:5,
    },
    // 是否显示标题
    title:{
      type:Boolean,
      default: true,
    },
    // 当图片加载时，显示的占位图片url（本地路径）,如果不指定，则默认先显示缩略图地址
    loading:{
      type:String,
      default: '',
    },
    // 图片加载404出错时，显示的占位图片
    load404:{
      type:String,
      default: ''
    },
    // 当只有一张图片时，是否自动隐藏相关按钮
    hideOnSingleImage:{
      type:Boolean,
      default: true
    },
    // 是否可以循环浏览
    loop:{
      type: Boolean,
      default: true
    },
    // 是否可见左右箭头
    pager:{
      type:Boolean,
      default:true,
    },
    // 是否开启画廊功能
    galleryBar:{
      type:Boolean,
      default:true
    }
  },
  beforeDestroy() {
    clearInterval(this.timer)
  },
  methods:{
    /***
     * 显示当前预览组件
     */
    previewImage(index=0){
      this.index = index
      this.show = true
    },

    /***
     * 打开跳转到第几张图片输入对话框
     */
    showIndexPrompt(){
      const res = prompt("请输入需要跳转第几张图片", this.preview.index + 1)
      const index = parseInt(res) - 1
      if(index >= 0){
        if(index < this.images.length){
          this.index = index
        }else{
          this.index = this.images.length - 1
        }
      }
    },

    stopPlay(){
      clearInterval(this.timer)
      this.timer = null
    },

    // 自动播放大图
    playImgs(evt, interval=5){
      console.log(interval)
      this.preview.timer = setInterval(()=>{
        this.nextImg()
      }, interval*1000)
    },

    /***
     * 移动放大镜
     */
    moveZoomer(evt){
      const {movementX, movementY} = evt
      switch (evt.buttons) {
        case 1:             // 表示鼠标左键被按下
          // console.log(movementX, movementY)
          this.top += movementY/10
          this.left += movementX/10
          break
      }
    },

    /***
     * 大图预览键盘事件监听
     */
    previewerKeyHandler(evt){
      console.log(evt)
    },

    /***
     * 缩放图片：处理鼠标滚轮事件
     * @param {WheelEvent} evt
     * @param {number | null} amount 缩放数量
     */
    scaleImg(evt={}, amount=null){
      const deltaY = evt.deltaY || amount
      if(deltaY < 0){                                         // 放大
        this.zoom += 0.1
      }else if(deltaY > 0 && this.zoom > 0.15){        // 缩小
        this.zoom -= 0.1
      }
      console.log(this.zoom)
    },

    // 切换上一张图片
    prevImg(){
      const index = this.index
      if(index > 0){
        this.index--
      }else if(this.loop){
        this.index = this.images.length - 1
      }
    },

    // 切换下一张图片
    nextImg(){
      const index = this.index
      if(index < this.images.length - 1){
        this.index++
      }else if(this.loop){
        this.index = 0
      }
    },

    // 旋转图片
    rotateImg(deg, relative=true){
      if(relative){
        this.rotate += deg
      }else{
        this.rotate = deg
      }
    },

    // 关闭大图预览
    closePreview(){
      this.$emit('close')
      clearInterval(this.timer)
      this.show = false
      this.timer = null
    },
  }
}
</script>

<style scoped>
.btn-preview-prev{
  position: fixed;
  left: 100px;
  top: 50%;
  z-index: 2001;
}

.btn-preview-next{
  position: fixed;
  right: 100px;
  top: 50%;
  z-index: 2001;
}

.image-viewer {
  z-index: 2000;
  position: fixed;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
}

.hg-images-previewer-back{
  background: rgba(0, 0, 0, 0.5);
}

.el-button.is-circle{
  padding: 5px!important;
  font-size: 30px;
}

.previewer-toolbox{
  background: rgba(0,0,0,0.5);
  padding: 0.25em 0.5em;
  border-radius: 999em;
  position: fixed;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);

  display: flex;
  justify-content: space-evenly;
}

.btn-circle{
  border-radius: 999em;
  padding: 5px;
  font-size: 30px;

  display: flex;
  align-items: center;
  justify-content: center;

  background: #909399;

  cursor: pointer;
}

.preview-gallery{
  position: fixed;

  right: 0;
  top:50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow-y: auto;
  max-height: 90vh;

  cursor: pointer;
}

.preview-gallery img{
  width: 100px;
  height: 100px;
  object-fit: cover;
  margin: 1px;
}

.btn-circle img{
  width: 30px;
  height: 30px;
  object-fit: contain;
}

.image-viewer .img{
  width: 100vw;
  height: 100vh;
  object-fit: scale-down;

  position: fixed;
  left: 50%;
  top:50%;
  transform: translate(-50%, -50%);

  transition: all 0.5s ease-in-out;
}

.image-viewer .icon{
  position: fixed;
  left: 50%;
  top:50%;
  transform: translate(-50%, -50%);
}


.previewer-title{
  position: fixed;
  top:0;
  left: 50%;
  transform: translateX(-50%);
  color:white;
  background: rgba(0,0,0,0.5);
  font-size: 30px;
  cursor: pointer;
}

.previewer-close{
  color: white;
  background: rgba(0,0,0,0.5);
  position: fixed;
  right:0;
  top:0;
  border-bottom-left-radius: 999em;
  padding: 10px;
  cursor: pointer;
}

.previewer-close img{
  width: 30px;
  height: 30px;
}

.zoomer{
  position: fixed;

  border-radius: 999em;
  object-fit: none;
  width: 100px;
  height: 100px;
}
</style>